package wsi.match;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.rygh.core.model.common.StringSet;
import org.rygh.core.xml.XmlPath;
import org.rygh.semantic.gui.swing.input.Input;
import org.rygh.semantic.model.ex.FactSet;
import org.rygh.semantic.model.rdf.Entity;

import wsi.dataguide.tree.WSXmlLeaf;
import wsi.dataguide.tree.WSXmlParser;
import wsi.dataguide.tree.WSXmlTree;
import wsi.function.DirManager;
import wsi.function.Function;
import wsi.subgraph.ExtractWorker;
import wsi.subgraph.tree.EntityLeaf;
import wsi.subgraph.tree.EntityTree;
import wsi.subgraph.tree.EntityTreeLoader;

public class Matcher {

	private Set<WSXmlLeaf> matchedLeaves;
	private List<XmlPath> idPredicateXmlPaths;

	public Matcher() {

	}

	public void clear() {
		if (matchedLeaves != null)
			matchedLeaves.clear();

		if (idPredicateXmlPaths != null)
			idPredicateXmlPaths.clear();
	}

	public Set<WSXmlLeaf> getMatchedLeaves() {
		return matchedLeaves;
	}

	public SubtreeMatch match(Input input, EntityTree entityTree, String xml) {
		if (xml == null || xml.trim().isEmpty()) {
			// System.out.println("no xml.");
			return null;
		}

		if (entityTree == null || entityTree.getConcreteRoot() == null) {
			// System.out.println("no entity tree.");
			return null;
		}

		WSXmlParser parser = new WSXmlParser(input, xml, true);

		// System.out.println("  begin to parse " + input);
		WSXmlTree tree = parser.parse();
		// System.out.println("  end parsing");

		if (!parser.isValid())
			return null;

		List<WSXmlLeaf> leaves = tree.getAllLeaves();

		return match(input, entityTree, null, leaves);
	}

	public SubtreeMatch match(Input input, EntityTree entityTree,
			WSXmlLeaf subroot, List<WSXmlLeaf> leaves) {

		// there are cases sup leaves are empty.
		if (leaves.isEmpty())
			return null;

		if (entityTree == null || entityTree.getConcreteRoot() == null)
			return null;

		// System.out.println("  begin to really match " + input);

		SubtreeMatch sm = new SubtreeMatch(input, subroot,
				entityTree.getConcreteRoot());

		// 131012 the following does not work. parent should have
		// more matches than children. even if children's max occur is larger.
		// MapList<String, MyXmlLeaf> xpathGroupedLeavesMap = new MapList<>();

		// gather leaves according to their xpaths without indices.
		// for (MyXmlLeaf leaf : leaves) {
		// String key = leaf.getXpath().stripIndex();
		// System.out.printf("match group %s, %s\n",
		// l.getXpath().getValue(),
		// key);

		// xpathGroupedLeavesMap.put(key, leaf);
		// }

		// for (Entry<String, List<MyXmlLeaf>> e :
		// xpathGroupedLeavesMap.getMap()
		// .entrySet()) {
		// MyXmlLeaf leaf = e.getValue().get(0);

		for (WSXmlLeaf leaf : leaves) {
			XmlPath lpath = leaf.getXpath();
			String str_path = lpath.stripIndex();

			if (idPredicateXmlPaths != null) {
				boolean skip = false;

				for (XmlPath p : idPredicateXmlPaths)
					if (p.stripIndex().equals(str_path)) {
						// System.out.printf("[info] skip leaf @ %s\n",
						// str_path);
						skip = true;
						break;
					}

				if (skip)
					continue;
			}

			String lastTag = lpath.getLastTag().toLowerCase();

			// System.out.printf("last tag %s @ %s\n", lastTag,
			// leaf.getXpath());

			/*
			 * code does not apply to geonames. we need author_id for isbndb get
			 * author by name.
			 */

			boolean exact = lastTag.contains("id") || lastTag.contains("isbn")
			/* || lastTag.contains("code") */;

			String source_value = leaf.getValue().getValue();
			assert source_value != null;

			List<EntityLeaf> snodes = entityTree.getNodes(source_value, exact);

			// System.out.println("source_value " + source_value + ":" +
			// snodes);

			if (snodes == null || snodes.isEmpty())
				continue;

			// if (lastTag.contains("id"))
			// System.out.println("last tag " + snodes + " subroot "
			// + subroot.getXpath());

			if (matchedLeaves != null)
				matchedLeaves.add(leaf);

			for (EntityLeaf n : snodes) {
				// we use a symbol for entity, not a value. the actual value is
				// to be resolved otherwise.
				//
				// if (s.isRoot()) {
				// // word order matters here
				//
				// StringSet s1 = new StringSet(s.getValue().getValue()
				// .getBodyString());
				// StringSet s2 = new StringSet(source_value);
				//
				// if (!s1.equalsOrdered(s2))
				// continue;
				// }

				MatchPair mp = new MatchPair(leaf, n);
				mp.setSuperNode(subroot == null);

				StringSet leaf_master = new StringSet(source_value);
				StringSet node_minor = new StringSet(n.getValue().getValue()
						.getValue());

				mp.setConfidence(leaf_master.getConfidence(node_minor));

				// mp.setProbability(1. / e.getValue().size());

				sm.add(mp);
			}
		}

		// System.out.println("  end really matching " + input);
		return sm;
	}

	/**
	 * instead of skipping nodes with large degree, we just load a partial
	 * subgraph with a limited degree
	 * 
	 * @param input
	 * @param function
	 * @return
	 */
	public EntityTree prepareEntityTree(Input input, Function function) {
		ExtractWorker extracter = new ExtractWorker(new DirManager(function),
				null);

		Entity entity = input.getEntity();

		// if (extracter.probeExtraction(entity)) {
		// System.out.printf("[warn] too large for entity %s, limit %d\n",
		// entity, SampleWorker.getDegreeLimit());
		//
		// return null;
		// }

		Map<Entity, FactSet> map = extracter.extract(entity);
		FactSet facts = new FactSet();

		for (FactSet j : map.values())
			facts.add(j);

		EntityTreeLoader loader = new EntityTreeLoader(entity, facts);
		EntityTree ent_tree = loader.load();

		return ent_tree;
	}

	public void setIdPredicateXmlPaths(List<XmlPath> idPredicateXmlPaths) {
		this.idPredicateXmlPaths = idPredicateXmlPaths;
	}

	public void setMatchedLeaves(Set<WSXmlLeaf> matchedLeaves) {
		this.matchedLeaves = matchedLeaves;
	}
}
